package org.jeecg.modules.pipe.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.constant.DeviceConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.device.entity.DevDeviceInfo;
import org.jeecg.modules.device.entity.DevDeviceRecord;
import org.jeecg.modules.device.mapper.DevDeviceInfoMapper;
import org.jeecg.modules.device.mapper.DevDeviceRecordMapper;
import org.jeecg.modules.drive.service.DriveCommonInterfaceService;
import org.jeecg.modules.pipe.entity.MonitorPoint;
import org.jeecg.modules.pipe.entity.PipeFlowLeakage;
import org.jeecg.modules.pipe.mapper.MonitorPointMapper;
import org.jeecg.modules.pipe.service.IMonitorPointService;
import org.jeecg.modules.pipe.service.IPipeFlowLeakageService;
import org.jeecg.modules.pipe.vo.PipeMarkerVo;
import org.jeecg.modules.pipe.vo.PipeNextFlowVo;
import org.jeecg.modules.pipe.vo.PipeQueryVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @Description: 监测点
 * @Author: jeecg-boot
 * @Date:   2024-05-18
 * @Version: V1.0
 */
@Service
public class MonitorPointServiceImpl extends ServiceImpl<MonitorPointMapper, MonitorPoint> implements IMonitorPointService {

    @Autowired
    private MonitorPointMapper monitorPointMapper;
    @Autowired
    private DevDeviceInfoMapper devDeviceInfoMapper;
    @Autowired
    private DevDeviceRecordMapper deviceRecordMapper;
    @Autowired
    private DriveCommonInterfaceService commonInterfaceService;
    @Autowired
    private IPipeFlowLeakageService pipeFlowLeakageService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ISysBaseAPI sysBaseAPI;

    @Override
    public List<PipeMarkerVo> getMonitorPointMarker(PipeQueryVo queryVo) {
        return monitorPointMapper.getMonitorPointMarker(queryVo);
    }

    /***
     * 获取监测点-监控设备
     * @param pointId
     * @return
     */
    @Override
    public JSONArray getMonitorDeviceList(String pointId) {
        List<DevDeviceInfo> list = devDeviceInfoMapper.selectList(Wrappers.<DevDeviceInfo>lambdaQuery()
                .eq(DevDeviceInfo::getDelFlag,0)
                .eq(DevDeviceInfo::getDeviceType,"5")
                .eq(DevDeviceInfo::getPointId,pointId));
        // 当前租户
        String tenantId = oConvertUtils.getString(TenantContext.getTenant(), "0");
        JSONArray recordVoLs=new JSONArray();
        for (DevDeviceInfo deviceInfo : list) {
            if("5".equals(deviceInfo.getDeviceType())){
                // 监控
                String playAddress = "";
                try {
                    // 获取监控设备播放地址，?? 需要替换成设备序列号
                    playAddress = commonInterfaceService.getMonitorDevicePlayByInterface(deviceInfo);
                    if(StringUtils.isNotEmpty(playAddress)){
                        playAddress = playAddress.replace("??", deviceInfo.getDeviceId());
                    }else {
                        throw new JeecgBootException("获取及时播放地址异常");
                    }
                    System.out.println(playAddress);
                }catch (Exception e){
                    throw new JeecgBootException(e.getMessage());
                }

                JSONObject hashMap = new JSONObject();
                hashMap.put("deviceId",deviceInfo.getDeviceId()); // 设备序列号
                hashMap.put("deviceType","5"); // 视频设备
                hashMap.put("videoType","4"); // 直连
                hashMap.put("deviceValue",playAddress); // 直连地址
                // hashMap.put("deviceValue",deviceInfo.getIpAddr()); // 直连地址
                recordVoLs.add(hashMap);
                continue;
            }
        }
        return recordVoLs;
    }

    /***
     * 获取监控设备-播放地址
     * @param deviceInfo
     */
    public void getMonMonitorDevicePlayUrl(DevDeviceInfo deviceInfo){
        if(null == deviceInfo){

        }
        String driveId = deviceInfo.getDriveId();


    }
    
    @Override
    public JSONArray getDeviceListReadTimeData(String pointId) {
        List<DevDeviceInfo> list = devDeviceInfoMapper.selectList(Wrappers.<DevDeviceInfo>lambdaQuery()
                .eq(DevDeviceInfo::getDelFlag,0).eq(DevDeviceInfo::getPointId,pointId));
        // 当前租户
        String tenantId = oConvertUtils.getString(TenantContext.getTenant(), "0");
        JSONArray recordVoLs=new JSONArray();
        for (DevDeviceInfo deviceInfo : list) {
            if("5".equals(deviceInfo.getDeviceType())){
                // 监控
                JSONObject hashMap = new JSONObject();
                hashMap.put("deviceId",deviceInfo.getDeviceId()); // 设备序列号
                hashMap.put("deviceType","5"); // 视频设备
                hashMap.put("videoType","4"); // 直连
                hashMap.put("deviceValue",deviceInfo.getIpAddr()); // 直连地址
                // hashMap.put("deviceValue",deviceInfo.getIpAddr()); // 直连地址
                recordVoLs.add(hashMap);
                continue;
            }

            /**3.缓存设备数据*/

            JSONObject devRealObj = getDeviceData(tenantId,deviceInfo.getDeviceId());
            if(null != devRealObj){
               recordVoLs.add(devRealObj);
            }else{
                DevDeviceRecord deviceRecord = deviceRecordMapper.selectOne(Wrappers.<DevDeviceRecord>lambdaQuery()
                        .eq(DevDeviceRecord::getDeviceId, deviceInfo.getDeviceId())
                        .eq(DevDeviceRecord::getDelFlag, 0)
                        .orderByDesc(DevDeviceRecord::getUpdateTime)
                        .last(" limit 1"));
                if(null != deviceRecord){
                    JSONObject hashMap = JSON.parseObject(JSON.toJSONString(deviceRecord), JSONObject.class);
                    recordVoLs.add(hashMap);
                }else{
                    DevDeviceRecord record = new DevDeviceRecord();
                    record.setDeviceId(deviceInfo.getDeviceId());
                    record.setDeviceUnit(deviceInfo.getDeviceUnit());
                    record.setDeviceValue("0");
                    record.setUpdateTime(new Date());
                    record.setDeviceType(deviceInfo.getDeviceType());
                    JSONObject hashMap = JSON.parseObject(JSON.toJSONString(record), JSONObject.class);
                    recordVoLs.add(hashMap);
                }
            }
        }
        return recordVoLs;
    }

    public JSONObject getDeviceData(String tenantId,String deviceId){
        Object devRealObj = redisUtil.hget(DeviceConstant.DEVICE_REALTIME_DATA + "::" + tenantId, deviceId);
        if(null != devRealObj) {
            JSONObject map = (JSONObject) devRealObj;
            if (null != map && map.size() > 0) {
                return map;
            }
        }

        DevDeviceRecord deviceRecord = deviceRecordMapper.selectOne(Wrappers.<DevDeviceRecord>lambdaQuery()
                .eq(DevDeviceRecord::getDeviceId, deviceId)
                .eq(DevDeviceRecord::getDelFlag, 0)
                .orderByDesc(DevDeviceRecord::getUpdateTime)
                .last(" limit 1"));

        JSONObject hashMap = JSON.parseObject(JSON.toJSONString(deviceRecord), JSONObject.class);
        return hashMap;
    }


    /***
     * 管段下级-流量设备
     */
    @Override
    public List<PipeFlowLeakage> pipeNextFlowDevice(PipeNextFlowVo queryVo){
        return monitorPointMapper.pipeNextFlowDevice(queryVo);
    }


    /**
     * 管段漏损分析- 定时任务
     */
    @Override
    public void pipeLeakageAnalysis(){
        /**
         * 1.找出所有管段（管段）left 监测点 left 设备
         * 2.监测点+设备 不为空 取设备数据及单位
         * 3.找出一级管段
         * 4.递归 归纳到子集
         * 5.递归 计算子业务数据和
         * 6.计算下一级设备数据和与当前设备数据差值， 问题：当前管段的下一级没有流量设备，再下一级或更多级有流量设备
         * 7.记录入库
         */
        // 当前租户
        String tenantId = oConvertUtils.getString(TenantContext.getTenant(), "0");

        /**1.找出所有管段（管段）left 监测点 left 设备*/
        List<PipeFlowLeakage> srcPipeFlowLeakageLs = this.pipeNextFlowDevice(null);
        // 一级管段集合
        List<PipeFlowLeakage> onePipeVoLs = new ArrayList<>();
        // 待保存的漏损流量表管段集
        List<PipeFlowLeakage> savePipeNextVoLs = new ArrayList<>();

        if(null != srcPipeFlowLeakageLs && srcPipeFlowLeakageLs.size() > 0){
            for (PipeFlowLeakage flowVo : srcPipeFlowLeakageLs) {
                // 有设备就是流量表
                if(StringUtils.isNotEmpty(flowVo.getPointId()) && StringUtils.isNotEmpty(flowVo.getDeviceId())){

                    /**2.监测点+设备 不为空 取设备数据及单位，为空则取下一级*/
                    flowVo.setIsBisDev(true); // 管段有监测点的流量设备
                    JSONObject devRealObj = this.getDeviceData(tenantId,flowVo.getDeviceId());
                    if(null != devRealObj){
                        flowVo.setReadData(devRealObj.getString("deviceValue"));
                        if(StringUtils.isNotEmpty(devRealObj.getString("deviceUnit"))){
                            flowVo.setUnitName(devRealObj.getString("deviceUnit"));
                        }
                    }
                }
            }

            /**3.找出一级管段*/
            for (PipeFlowLeakage flowVo : srcPipeFlowLeakageLs) {
                if ("0".equals(flowVo.getPid())) {
                    onePipeVoLs.add(flowVo);
                }
            }

            /**4.递归 归纳到子集*/
            if(onePipeVoLs.size()>0){
                this.recursionNextPipe(srcPipeFlowLeakageLs,onePipeVoLs);
            }

            /**5.递归 下一级-数据*/
            if(onePipeVoLs.size()>0){
                this.recursionNextPipeData(onePipeVoLs);
            }

            /**6.计算下一级设备数据和与当前设备数据差值*/
            // 漏损误差值
            String dictLeakageVal = "0";
            List<DictModel> dictItems = sysBaseAPI.getDictItems("pipe_flow_leakage");
            for (DictModel dictItem : dictItems) {
                String text = dictItem.getText();
                if("next_leakage".equals(text)){
                    // 下级管段与上级之间 漏损值 大于则表示有效漏损
                    dictLeakageVal = dictItem.getValue();
                }
            }

            // 递归计算管段的流量设备漏损集
            this.recursionNextPipeLeakage(savePipeNextVoLs,onePipeVoLs,dictLeakageVal);

            /**7.记录入库*/
            pipeFlowLeakageService.saveBatch(savePipeNextVoLs);
        }
    }


    /***
     * 递归计算管段的流量设备漏损集
     * @param savePipeNextLs
     * @param pipeNextLs
     * @param dictLeakageVal
     */
    public void recursionNextPipeLeakage(List<PipeFlowLeakage> savePipeNextLs,List<PipeFlowLeakage> pipeNextLs,String dictLeakageVal){
        if(null == pipeNextLs || pipeNextLs.size() == 0){
            return;
        }

        for (PipeFlowLeakage flowVo : pipeNextLs) {
            if( null != flowVo.getIsBisDev() && flowVo.getIsBisDev() && StringUtils.isNotEmpty(flowVo.getReadData())){
                BigDecimal subtract = new BigDecimal(flowVo.getReadData()).subtract(null != flowVo.getNextSumData() ? flowVo.getNextSumData() : BigDecimal.ZERO);

                if(subtract.compareTo(new BigDecimal(dictLeakageVal)) >= 0){
                    if(null != flowVo.getNextSumData()) {
                        // 差值大于漏损值 则算有效漏损
                        savePipeNextLs.add(flowVo);
                    }
                }

                if(null != flowVo.getChildren() && flowVo.getChildren().size() > 0){
                    recursionNextPipeLeakage(savePipeNextLs,flowVo.getChildren(),dictLeakageVal);
                }
            }else{
                if(null != flowVo.getChildren() && flowVo.getChildren().size() > 0){
                    recursionNextPipeLeakage(savePipeNextLs,flowVo.getChildren(),dictLeakageVal);
                }
            }
        }
    }


    /**
     * 递归管段子集
     *
     * @param srcPipeLs 所有管段
     * @param onePipeLs 一级管段
     */
    public void recursionNextPipe(List<PipeFlowLeakage> srcPipeLs,List<PipeFlowLeakage> onePipeLs){
         for (PipeFlowLeakage flowVo : onePipeLs) {
            if("0".equals(flowVo.getPid())){
                List<PipeFlowLeakage> nextPipeLs = this.getNextPipe(srcPipeLs, flowVo);
                if(null == nextPipeLs && nextPipeLs.size() == 0){
                    continue;
                }
                flowVo.setChildren(nextPipeLs);
                recursionNextPipe(srcPipeLs,nextPipeLs);
            }else{
                List<PipeFlowLeakage> nextPipeLs = this.getNextPipe(srcPipeLs, flowVo);
                if(null == nextPipeLs || nextPipeLs.size() == 0){
                    continue;
                }
                flowVo.setChildren(nextPipeLs);
                recursionNextPipe(srcPipeLs,nextPipeLs);
            }
        }
    }

    /**遍历当前管段的下一级管段*/
    public List<PipeFlowLeakage> getNextPipe(List<PipeFlowLeakage> pipeLs,PipeFlowLeakage currentVo){
        List<PipeFlowLeakage>rsLs = new ArrayList<PipeFlowLeakage>();
        boolean isExistNextPipe = false;
        for (PipeFlowLeakage flowVo : pipeLs) {
            if(null != flowVo.getPid() && flowVo.getPid().equals(currentVo.getPipeId())){
               rsLs.add(flowVo);
               isExistNextPipe = true;
            }
        }
        if(!isExistNextPipe){
            return null;
        }
        return rsLs;
    }

    /**
     * 递归管段子集- 数据
     *
     * @param pipeTreeLs 管段树
     */
    public void recursionNextPipeData(List<PipeFlowLeakage> pipeTreeLs){
        for (PipeFlowLeakage flowVo : pipeTreeLs) {
            if(null != flowVo.getIsBisDev() && null != flowVo.getChildren() && flowVo.getChildren().size() > 0){
                BigDecimal nextData = new BigDecimal(0.0);
                // 有业务数据设备节点集
                StringBuilder sb = new StringBuilder();
                for (PipeFlowLeakage child : flowVo.getChildren()) {
                    if(null != child.getIsBisDev() && null != child.getReadData()) {
                        nextData = nextData.add(new BigDecimal(child.getReadData())).setScale(2, BigDecimal.ROUND_UP);
                        sb = sb.append(","+child.getPipeId());
                    }else{
                        // 当前级没有，一直递归找到下级流量表数据，每个分支必须找到一个流量设备，直到叶子，除非没有
                        getNextPipeData(child.getChildren(),flowVo);
                    }
                }
                if(null != flowVo.getNextSumData() && flowVo.getNextSumData().compareTo(BigDecimal.ZERO) > 0){
                    flowVo.setNextSumData(flowVo.getNextSumData().add(nextData));
                }else if(nextData.compareTo(BigDecimal.ZERO) > 0){
                    flowVo.setNextSumData(nextData);
                }
                if( StringUtils.isNotEmpty(flowVo.getChildrenNode())){
                    flowVo.setChildrenNode( flowVo.getChildrenNode()+sb.toString());
                }else if( StringUtils.isNotEmpty(sb.toString())){
                    flowVo.setChildrenNode( sb.toString());
                }
                recursionNextPipeData(flowVo.getChildren());
            }else{
                if(null != flowVo.getChildren() && flowVo.getChildren().size() > 0){
                    recursionNextPipeData(flowVo.getChildren());
                }
            }
        }
    }



    /**遍历当前管段的下一级管段-数据,每个子管段必须找到一个流量表*/
    public void getNextPipeData(List<PipeFlowLeakage> pipeLs,PipeFlowLeakage currentVo){
        BigDecimal nextData = new BigDecimal(0.0);
        boolean isExistNextPipe = false;
        if(null != pipeLs && pipeLs.size()>0){
            for (PipeFlowLeakage flowVo : pipeLs) {
                if(null != flowVo.getReadData()){
                    nextData = nextData.add(new BigDecimal(flowVo.getReadData())).setScale(2,BigDecimal.ROUND_UP);
                    isExistNextPipe = true;
                }else{
                    getNextPipeData(flowVo.getChildren(),currentVo);
                }
            }
            if(isExistNextPipe){
                if(null != currentVo.getNextSumData()){
                    currentVo.setNextSumData(currentVo.getNextSumData().add(nextData));
                }else{
                    currentVo.setNextSumData(nextData);
                }
            }
        }
    }

}
